
#include "generic.h"
#include "sdcard.h"
#include "mmc.h"

uint8_t spiRec(){
  return SPI_RxByte();
}
void spiSend(uint8_t data){
  SPI_TxByte(data);
}

void chipSelectHigh(){
  SPI_ChipDeselect();
}
void chipSelectLow() {
  SPI_ChipSelect();
}


// void printType(){
//   switch(type_){
//     case 1:printf("sd1\r\n");break;
//     case 2:printf("sd2\r\n");break;
//     case 3:printf("sdhc\r\n");break;
//     case 4:printf("mmc\r\n");break;
//   }
// }
int type_;
int error;
int status_;

int _readBlock(unsigned int block, char* dst) {
  int n;

  if (type_!= SD_CARD_TYPE_SDHC) block <<= 9;
  if (cardCommand(CMD17, block)) {
    error = SD_CARD_ERROR_CMD17;
    goto fail;
  }
  if (!waitStartBlock()) {
    goto fail;
  }

  for (int i = 0; i < 512; i++) {
   dst[i] = spiRec();
  }
  
  readEnd();
  
  return 1;

 fail:
  chipSelectHigh();
  return 0;
}

int _writeBlock(unsigned int blockNumber, const char* src) {

  if (type_ != SD_CARD_TYPE_SDHC) blockNumber <<= 9;
  if (cardCommand(CMD24, blockNumber)) {
    error = SD_CARD_ERROR_CMD24;
    goto fail;
  }
  if (!writeData(DATA_START_BLOCK, src)) goto fail;

  // wait for flash programming to complete
  if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
    error = SD_CARD_ERROR_WRITE_TIMEOUT;
    goto fail;
  }
  // response is r2 so get and check two bytes for nonzero
  if (cardCommand(CMD13, 0) || spiRec()) {
    error = SD_CARD_ERROR_WRITE_PROGRAMMING;
    goto fail;
  }
  chipSelectHigh();
  return 1;

 fail:
  chipSelectHigh();
  return 0;
}

void readEnd(void) {
  spiRec();
  spiRec();
  chipSelectHigh();
}

// private functions
int cardAcmd(int cmd, unsigned int arg) {
  cardCommand(CMD55, 0);
  return cardCommand(cmd, arg);
}

int waitNotBusy(int timeoutMillis) {
  int i = 0;
  do {
		DWT_Delay_ms(1);
    if (spiRec() == 0XFF) return 1;
    i++;
  }
  while (i < timeoutMillis);
  return 0;
}

int writeData(int token, const char* src) {
  spiSend(token);
  for (int i = 0; i < 512; i++) {
    spiSend(src[i]);
  }
  spiSend(0xff);  // dummy crc
  spiSend(0xff);  // dummy crc

  status_ = spiRec();
  if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
    error = SD_CARD_ERROR_WRITE;
    chipSelectHigh();
    return 0;
  }
  return 1;
}

///////////////////////////////////////////////////
int cardCommand(int cmd, unsigned int arg) {
  // end read if in partialBlockRead mode
  readEnd();

  // select card
  chipSelectLow();

  // wait up to 300 ms if busy
  waitNotBusy(300);

  // send command
  spiSend(cmd | 0x40);

  int s;
  // send argument
  for (s = 24; s >= 0; s -= 8) spiSend(arg >> s);

  // send CRC
  int crc = 0XFF;
  if (cmd == CMD0) crc = 0X95;  // correct crc for CMD0 with arg 0
  if (cmd == CMD8) crc = 0X87;  // correct crc for CMD8 with arg 0X1AA
  spiSend(crc);

  // wait for response
  int i;
  for (i = 0; ((status_ = spiRec()) & 0X80) && i != 0XFF; i++)
    ;
  
  return status_;
}

int cardCommand2(int cmd) {
  // end read if in partialBlockRead mode
  readEnd();

  // select card
  chipSelectLow();

  // wait up to 300 ms if busy
  waitNotBusy(300);

  // send command
  spiSend(cmd | 0x40);

  // send CRC
  int crc = 0XFF;
  if (cmd == CMD0) crc = 0X95;  // correct crc for CMD0 with arg 0
  if (cmd == CMD8) crc = 0X87;  // correct crc for CMD8 with arg 0X1AA
  spiSend(crc);

  return 0;
}



int getSize() {
  uint8_t csd[16];
  uint8_t v;
  cardCommand2(CMD9);
  for(int i=0;i<100;i++){
    v = spiRec();
    //printf("v: %d\r\n", v);
    if(v==254)break;
  }
  for(int i=0;i<16;i++){
    v = spiRec();
    //printf("csd[i]: %d\r\n", v);
    csd[i] = v;
  }
  v = spiRec();
  //printf("v: %d\r\n", v);
  v = spiRec();
  //printf("v: %d\r\n", v);

  uint32_t size;

  int csize;
  int Capacity;
  
  if((csd[0]&0xC0)==0x40)//判断bit126是否为1
  { 
    csize = csd[9] + ((uint32_t)csd[8] << 8) + ((uint32_t)(csd[7] & 63) << 16) + 1;
    Capacity = csize << 19; 
  }
  else
  { 
    int n = (csd[5] & 0x0F) + ((csd[10] & 0x80) >> 7) + ((csd[9] & 0x03) << 1) + 2;
    csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 0x03) << 10) + 1;
    Capacity = (uint32_t)csize << (n);
  }
  //printf("Capacity: %d\r\n", Capacity);

  chipSelectHigh();
  return Capacity;
}

int initError;
int sdcardinit() {
  initError = 0;
  
  unsigned int arg;
  
  error = 0;
  //inBlock_ = 0;
  //partialBlockRead_ = 0;
  type_ = 0;
  
  //offset_=0;
  //IOWR(MYTIMER32, 0, 0);
  
  error = type_ = 0;//inBlock_ =  partialBlockRead_ = 
  // 16-bit init start time allows over a minute
  int t0 = 0;

  chipSelectHigh();
  
  // must supply min of 74 clock cycles with CS high.
  for (int i = 0; i < 20; i++) spiSend(0XFF);


  // command to go idle in SPI mode
  int ok = 0;
  //for(int i=0;i<16;i++){
  //  polpha_ = i;
    chipSelectLow();
    
    for(int i=0;i<5;i++){
      status_ = cardCommand(CMD0, 0);
      //printf("status_1 %d\r\n", status_);
      if(status_ == R1_IDLE_STATE){
        ok = 1;
        break;
      }else{
      }
      DWT_Delay_ms(100);
    }
    //if(ok){
      //print("polpha=");printInt(polpha_);print("\r\n");
    //  break;
    //}
  //}
  if(!ok){
    initError = 1;
    goto fail;
  }
  //while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
  //  if (((int)(millis() - t0)) > SD_INIT_TIMEOUT) {
  //    initError = 1;
  //    goto fail;
  //  }
  //}
  // check SD version
  status_ = cardCommand(CMD8, 0x1AA);
  //printf("status_CMD8 %d\r\n", status_);
  if (status_ & R1_ILLEGAL_COMMAND) {


      int t0 = 0;
      while(1){
        status_ = cardCommand(CMD1, 0);
        if(status_ == R1_READY_STATE) {
          type_ = SD_CARD_TYPE_MMC;
          chipSelectHigh();
          return 1;
        }
        DWT_Delay_ms(1);
        if(t0>300){
          break;
        }
      }

    type_ = SD_CARD_TYPE_SD1;
  } else {
    // only need last byte of r7 response
    for (int i = 0; i < 4; i++) status_ = spiRec();
    if (status_ != 0XAA) {
      initError = 2;
      goto fail;
    }
    type_ = SD_CARD_TYPE_SD2;
  }
  // initialize card and send host supports SDHC if SD2
  arg = type_ == SD_CARD_TYPE_SD2 ? 0X40000000 : 0;

  t0=0;
  while (1) {
    status_ = cardAcmd(ACMD41, arg);
    //printf("status_ACMD41 %d\r\n", status_);
    if(status_ == R1_READY_STATE)break;
    // check for timeout
    DWT_Delay_ms(1);
    t0++;
    if (t0 > SD_INIT_TIMEOUT) {
      initError = 3;
      goto fail;
    }
  }
  // if SD2 read OCR register to check for SDHC card
  if (type_ == SD_CARD_TYPE_SD2) {
    if (cardCommand(CMD58, 0)) {
      initError = 4;
      goto fail;
    }
    if ((spiRec() & 0XC0) == 0XC0) type_ = SD_CARD_TYPE_SDHC;
    // discard rest of ocr - contains allowed voltage range
    for (int i = 0; i < 3; i++) spiRec();
  }
  chipSelectHigh();

  return 1;

 fail:
  chipSelectHigh();
  return 0;

};

int waitStartBlock(void) {
  int t0 = 0;
  while ((status_ = spiRec()) == 0XFF) {
    if (t0 > SD_READ_TIMEOUT) {
      initError = 5;
      goto fail;
    }
    t0++;
    DWT_Delay_ms(1);
  }
  if (status_ != DATA_START_BLOCK) {
    initError = 6;
    goto fail;
  }
  return 1;

 fail:
  chipSelectHigh();
  return 0;
}
